#!/bin/bash

# 系统健康检查和监控脚本
# System Health Check and Monitoring Script

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# 配置变量
SCRIPT_VERSION="1.0.0"
PROJECT_NAME="personal-cloud-notes"
PROJECT_DIR="/var/www/$PROJECT_NAME"
MONITOR_LOG="/var/log/${PROJECT_NAME}-monitor.log"
ALERT_LOG="/var/log/${PROJECT_NAME}-alerts.log"
PM2_USER="pm2"

# 监控阈值配置
CPU_ALERT_THRESHOLD=80
MEMORY_ALERT_THRESHOLD=85
DISK_ALERT_THRESHOLD=90
RESPONSE_TIME_ALERT_THRESHOLD=5000
ERROR_RATE_ALERT_THRESHOLD=5
CONNECTION_TIMEOUT=10

# 通知配置
ENABLE_EMAIL_ALERTS=false
ENABLE_WEBHOOK_ALERTS=false
EMAIL_RECIPIENT="admin@example.com"
WEBHOOK_URL=""

# 函数：打印带颜色的消息
print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> "$MONITOR_LOG"
}

# 函数：记录警报
log_alert() {
    local level=$1
    local message=$2
    local alert_time=$(date '+%Y-%m-%d %H:%M:%S')
    
    echo "$alert_time [$level] $message" >> "$ALERT_LOG"
    
    case $level in
        CRITICAL)
            print_message $RED "🚨 严重警报: $message"
            send_alert "CRITICAL" "$message"
            ;;
        WARNING)
            print_message $YELLOW "⚠️  警告: $message"
            send_alert "WARNING" "$message"
            ;;
        INFO)
            print_message $BLUE "ℹ️  信息: $message"
            ;;
    esac
}

# 函数：发送警报通知
send_alert() {
    local level=$1
    local message=$2
    
    # 邮件通知
    if [ "$ENABLE_EMAIL_ALERTS" = true ] && command -v mail >/dev/null 2>&1; then
        echo "$message" | mail -s "个人云笔记系统警报 - $level" "$EMAIL_RECIPIENT" 2>/dev/null || true
    fi
    
    # Webhook通知
    if [ "$ENABLE_WEBHOOK_ALERTS" = true ] && [ -n "$WEBHOOK_URL" ] && command -v curl >/dev/null 2>&1; then
        local payload="{\"level\":\"$level\",\"message\":\"$message\",\"timestamp\":\"$(date -Iseconds)\",\"service\":\"$PROJECT_NAME\"}"
        curl -X POST -H "Content-Type: application/json" -d "$payload" "$WEBHOOK_URL" >/dev/null 2>&1 || true
    fi
}

# 函数：检查CPU使用率
check_cpu_usage() {
    local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//' | cut -d'%' -f1)
    
    # 如果top命令格式不同，尝试其他方法
    if [ -z "$cpu_usage" ] || ! [[ "$cpu_usage" =~ ^[0-9]+\.?[0-9]*$ ]]; then
        cpu_usage=$(sar -u 1 1 2>/dev/null | tail -1 | awk '{print 100-$8}' | cut -d'.' -f1 2>/dev/null || echo "0")
    fi
    
    # 如果还是获取不到，使用vmstat
    if [ -z "$cpu_usage" ] || ! [[ "$cpu_usage" =~ ^[0-9]+\.?[0-9]*$ ]]; then
        cpu_usage=$(vmstat 1 2 | tail -1 | awk '{print 100-$15}' 2>/dev/null || echo "0")
    fi
    
    cpu_usage=${cpu_usage%.*} # 去掉小数部分
    
    if [ "$cpu_usage" -gt "$CPU_ALERT_THRESHOLD" ]; then
        log_alert "WARNING" "CPU使用率过高: ${cpu_usage}%"
        return 1
    else
        print_message $GREEN "CPU使用率正常: ${cpu_usage}%"
        return 0
    fi
}

# 函数：检查内存使用率
check_memory_usage() {
    local memory_info=$(free -m | awk 'NR==2{printf "%.0f %.0f %.0f", $3*100/$2, $3, $2}')
    local memory_usage=$(echo $memory_info | awk '{print $1}')
    local used_mb=$(echo $memory_info | awk '{print $2}')
    local total_mb=$(echo $memory_info | awk '{print $3}')
    
    if [ "$memory_usage" -gt "$MEMORY_ALERT_THRESHOLD" ]; then
        log_alert "WARNING" "内存使用率过高: ${memory_usage}% (${used_mb}MB/${total_mb}MB)"
        return 1
    else
        print_message $GREEN "内存使用率正常: ${memory_usage}% (${used_mb}MB/${total_mb}MB)"
        return 0
    fi
}

# 函数：检查磁盘使用率
check_disk_usage() {
    local disk_info=$(df / | awk 'NR==2{printf "%s %s", $5, $4}')
    local disk_usage=$(echo $disk_info | awk '{print $1}' | sed 's/%//')
    local available_gb=$(echo $disk_info | awk '{print int($2/1024/1024)}')
    
    if [ "$disk_usage" -gt "$DISK_ALERT_THRESHOLD" ]; then
        log_alert "CRITICAL" "磁盘空间不足: ${disk_usage}% 已使用，剩余 ${available_gb}GB"
        return 1
    elif [ "$disk_usage" -gt 80 ]; then
        log_alert "WARNING" "磁盘空间警告: ${disk_usage}% 已使用，剩余 ${available_gb}GB"
        return 1
    else
        print_message $GREEN "磁盘空间充足: ${disk_usage}% 已使用，剩余 ${available_gb}GB"
        return 0
    fi
}

# 函数：检查系统负载
check_system_load() {
    local load_avg=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
    local cpu_cores=$(nproc)
    local load_percent=$(echo "$load_avg * 100 / $cpu_cores" | bc -l | cut -d'.' -f1)
    
    if [ "$load_percent" -gt 90 ]; then
        log_alert "CRITICAL" "系统负载过高: $load_avg (${load_percent}%)"
        return 1
    elif [ "$load_percent" -gt 70 ]; then
        log_alert "WARNING" "系统负载较高: $load_avg (${load_percent}%)"
        return 1
    else
        print_message $GREEN "系统负载正常: $load_avg (${load_percent}%)"
        return 0
    fi
}

# 函数：检查服务状态
check_service_status() {
    local failed_services=()
    
    # 检查Nginx
    if ! systemctl is-active --quiet nginx; then
        failed_services+=("nginx")
        log_alert "CRITICAL" "Nginx服务未运行"
    fi
    
    # 检查MySQL
    if ! systemctl is-active --quiet mysql 2>/dev/null && ! systemctl is-active --quiet mysqld 2>/dev/null; then
        failed_services+=("mysql")
        log_alert "CRITICAL" "MySQL服务未运行"
    fi
    
    # 检查PM2应用
    if ! sudo -u $PM2_USER pm2 list 2>/dev/null | grep -q "$PROJECT_NAME.*online"; then
        failed_services+=("pm2-app")
        log_alert "CRITICAL" "PM2应用未运行"
    fi
    
    if [ ${#failed_services[@]} -eq 0 ]; then
        print_message $GREEN "所有服务运行正常"
        return 0
    else
        return 1
    fi
}

# 函数：检查端口监听
check_port_listening() {
    local failed_ports=()
    local required_ports=(80 443 3000 3306)
    
    for port in "${required_ports[@]}"; do
        if ! netstat -tlnp 2>/dev/null | grep -q ":$port.*LISTEN" && ! ss -tlnp 2>/dev/null | grep -q ":$port.*LISTEN"; then
            failed_ports+=($port)
        fi
    done
    
    if [ ${#failed_ports[@]} -eq 0 ]; then
        print_message $GREEN "所有必要端口正常监听"
        return 0
    else
        log_alert "CRITICAL" "端口监听异常: ${failed_ports[*]}"
        return 1
    fi
}

# 函数：检查数据库连接
check_database_connection() {
    if [ -f "$PROJECT_DIR/.env" ]; then
        local db_password=$(grep "DB_PASSWORD=" "$PROJECT_DIR/.env" | cut -d'=' -f2)
        local db_user=$(grep "DB_USER=" "$PROJECT_DIR/.env" | cut -d'=' -f2)
        local db_name=$(grep "DB_NAME=" "$PROJECT_DIR/.env" | cut -d'=' -f2)
        
        if timeout 10 mysql -u"$db_user" -p"$db_password" -e "USE $db_name; SELECT 1;" >/dev/null 2>&1; then
            print_message $GREEN "数据库连接正常"
            return 0
        else
            log_alert "CRITICAL" "数据库连接失败"
            return 1
        fi
    else
        log_alert "WARNING" "数据库配置文件未找到"
        return 1
    fi
}

# 函数：检查HTTP响应时间
check_http_response_time() {
    local start_time=$(date +%s%3N)
    local http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout $CONNECTION_TIMEOUT --max-time $CONNECTION_TIMEOUT "http://localhost:3000" 2>/dev/null || echo "000")
    local end_time=$(date +%s%3N)
    local response_time=$((end_time - start_time))
    
    if [ "$http_code" = "000" ]; then
        log_alert "CRITICAL" "HTTP服务无响应"
        return 1
    elif [ "$response_time" -gt "$RESPONSE_TIME_ALERT_THRESHOLD" ]; then
        log_alert "WARNING" "HTTP响应时间过长: ${response_time}ms (状态码: $http_code)"
        return 1
    elif [[ ! "$http_code" =~ ^(200|301|302)$ ]]; then
        log_alert "WARNING" "HTTP响应状态异常: $http_code"
        return 1
    else
        print_message $GREEN "HTTP响应正常: ${response_time}ms (状态码: $http_code)"
        return 0
    fi
}

# 函数：检查Nginx代理
check_nginx_proxy() {
    local start_time=$(date +%s%3N)
    local http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout $CONNECTION_TIMEOUT --max-time $CONNECTION_TIMEOUT "http://localhost" 2>/dev/null || echo "000")
    local end_time=$(date +%s%3N)
    local response_time=$((end_time - start_time))
    
    if [ "$http_code" = "000" ]; then
        log_alert "CRITICAL" "Nginx代理无响应"
        return 1
    elif [ "$response_time" -gt "$RESPONSE_TIME_ALERT_THRESHOLD" ]; then
        log_alert "WARNING" "Nginx代理响应时间过长: ${response_time}ms"
        return 1
    elif [[ ! "$http_code" =~ ^(200|301|302)$ ]]; then
        log_alert "WARNING" "Nginx代理响应状态异常: $http_code"
        return 1
    else
        print_message $GREEN "Nginx代理正常: ${response_time}ms (状态码: $http_code)"
        return 0
    fi
}

# 函数：检查日志错误率
check_log_error_rate() {
    local log_file="$PROJECT_DIR/data/logs/app.log"
    local error_log="$PROJECT_DIR/data/logs/error.log"
    
    if [ -f "$log_file" ] || [ -f "$error_log" ]; then
        # 检查最近5分钟的错误日志
        local recent_errors=0
        local recent_total=0
        
        # 计算最近5分钟的时间戳
        local five_minutes_ago=$(date -d '5 minutes ago' '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -v-5M '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "")
        
        if [ -n "$five_minutes_ago" ]; then
            if [ -f "$log_file" ]; then
                recent_total=$(grep "$five_minutes_ago" "$log_file" 2>/dev/null | wc -l || echo 0)
            fi
            
            if [ -f "$error_log" ]; then
                recent_errors=$(grep "$five_minutes_ago" "$error_log" 2>/dev/null | wc -l || echo 0)
            fi
            
            if [ "$recent_total" -gt 0 ]; then
                local error_rate=$((recent_errors * 100 / recent_total))
                
                if [ "$error_rate" -gt "$ERROR_RATE_ALERT_THRESHOLD" ]; then
                    log_alert "WARNING" "错误率过高: ${error_rate}% (${recent_errors}/${recent_total})"
                    return 1
                else
                    print_message $GREEN "错误率正常: ${error_rate}% (${recent_errors}/${recent_total})"
                    return 0
                fi
            else
                print_message $BLUE "最近5分钟无日志记录"
                return 0
            fi
        else
            print_message $YELLOW "无法计算时间，跳过错误率检查"
            return 0
        fi
    else
        print_message $YELLOW "日志文件不存在，跳过错误率检查"
        return 0
    fi
}

# 函数：检查SSL证书有效期
check_ssl_certificate() {
    local ssl_cert_path="/etc/nginx/ssl/personal-cloud-notes.crt"
    
    if [ -f "$ssl_cert_path" ]; then
        local expiry_date=$(openssl x509 -enddate -noout -in "$ssl_cert_path" 2>/dev/null | cut -d= -f2)
        
        if [ -n "$expiry_date" ]; then
            local expiry_timestamp=$(date -d "$expiry_date" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$expiry_date" +%s 2>/dev/null || echo 0)
            local current_timestamp=$(date +%s)
            local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
            
            if [ "$days_until_expiry" -lt 7 ]; then
                log_alert "CRITICAL" "SSL证书即将过期: $days_until_expiry 天后过期"
                return 1
            elif [ "$days_until_expiry" -lt 30 ]; then
                log_alert "WARNING" "SSL证书即将过期: $days_until_expiry 天后过期"
                return 1
            else
                print_message $GREEN "SSL证书有效: $days_until_expiry 天后过期"
                return 0
            fi
        else
            log_alert "WARNING" "无法读取SSL证书过期时间"
            return 1
        fi
    else
        print_message $BLUE "SSL证书文件不存在，跳过检查"
        return 0
    fi
}

# 函数：检查备份状态
check_backup_status() {
    local backup_dir="$PROJECT_DIR/data/backups"
    
    if [ -d "$backup_dir" ]; then
        # 检查最近24小时内是否有备份
        local recent_backup=$(find "$backup_dir" -name "*.sql*" -mtime -1 2>/dev/null | head -1)
        
        if [ -n "$recent_backup" ]; then
            local backup_age=$(stat -c %Y "$recent_backup" 2>/dev/null || stat -f %m "$recent_backup" 2>/dev/null || echo 0)
            local current_time=$(date +%s)
            local hours_since_backup=$(( (current_time - backup_age) / 3600 ))
            
            if [ "$hours_since_backup" -gt 48 ]; then
                log_alert "WARNING" "备份过期: 最近备份是 $hours_since_backup 小时前"
                return 1
            else
                print_message $GREEN "备份状态正常: 最近备份是 $hours_since_backup 小时前"
                return 0
            fi
        else
            log_alert "WARNING" "未找到最近的备份文件"
            return 1
        fi
    else
        log_alert "WARNING" "备份目录不存在"
        return 1
    fi
}

# 函数：生成健康报告
generate_health_report() {
    local report_file="/tmp/${PROJECT_NAME}-health-report-$(date +%Y%m%d-%H%M%S).json"
    local timestamp=$(date -Iseconds)
    
    # 收集系统信息
    local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//' | cut -d'%' -f1 2>/dev/null || echo "0")
    local memory_info=$(free -m | awk 'NR==2{printf "%s %s %s", $3*100/$2, $3, $2}')
    local disk_info=$(df / | awk 'NR==2{printf "%s %s", $5, $4}')
    local load_avg=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
    
    # 生成JSON报告
    cat > "$report_file" << EOF
{
  "timestamp": "$timestamp",
  "service": "$PROJECT_NAME",
  "status": "healthy",
  "metrics": {
    "cpu": {
      "usage_percent": ${cpu_usage%.*},
      "threshold": $CPU_ALERT_THRESHOLD
    },
    "memory": {
      "usage_percent": $(echo $memory_info | awk '{print $1}'),
      "used_mb": $(echo $memory_info | awk '{print $2}'),
      "total_mb": $(echo $memory_info | awk '{print $3}'),
      "threshold": $MEMORY_ALERT_THRESHOLD
    },
    "disk": {
      "usage_percent": $(echo $disk_info | awk '{print $1}' | sed 's/%//'),
      "available_gb": $(echo $disk_info | awk '{print int($2/1024/1024)}'),
      "threshold": $DISK_ALERT_THRESHOLD
    },
    "load": {
      "average": "$load_avg",
      "cores": $(nproc)
    }
  },
  "services": {
    "nginx": "$(systemctl is-active nginx 2>/dev/null || echo 'inactive')",
    "mysql": "$(systemctl is-active mysql 2>/dev/null || systemctl is-active mysqld 2>/dev/null || echo 'inactive')",
    "pm2_app": "$(sudo -u $PM2_USER pm2 list 2>/dev/null | grep -q "$PROJECT_NAME.*online" && echo 'active' || echo 'inactive')"
  },
  "checks": {
    "http_response": "$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 5 "http://localhost:3000" 2>/dev/null || echo "000")",
    "database_connection": "$(timeout 5 mysql -u$(grep "DB_USER=" "$PROJECT_DIR/.env" | cut -d'=' -f2) -p$(grep "DB_PASSWORD=" "$PROJECT_DIR/.env" | cut -d'=' -f2) -e "SELECT 1;" >/dev/null 2>&1 && echo 'ok' || echo 'failed')"
  }
}
EOF
    
    print_message $GREEN "健康报告已生成: $report_file"
    echo "$report_file"
}

# 函数：运行完整健康检查
run_full_health_check() {
    print_message $CYAN "开始系统健康检查..."
    
    local failed_checks=0
    local total_checks=0
    
    # 系统资源检查
    ((total_checks++)); check_cpu_usage || ((failed_checks++))
    ((total_checks++)); check_memory_usage || ((failed_checks++))
    ((total_checks++)); check_disk_usage || ((failed_checks++))
    ((total_checks++)); check_system_load || ((failed_checks++))
    
    # 服务状态检查
    ((total_checks++)); check_service_status || ((failed_checks++))
    ((total_checks++)); check_port_listening || ((failed_checks++))
    ((total_checks++)); check_database_connection || ((failed_checks++))
    
    # 应用响应检查
    ((total_checks++)); check_http_response_time || ((failed_checks++))
    ((total_checks++)); check_nginx_proxy || ((failed_checks++))
    
    # 其他检查
    ((total_checks++)); check_log_error_rate || ((failed_checks++))
    ((total_checks++)); check_ssl_certificate || ((failed_checks++))
    ((total_checks++)); check_backup_status || ((failed_checks++))
    
    # 检查总结
    local success_rate=$(( (total_checks - failed_checks) * 100 / total_checks ))
    
    print_message $CYAN "健康检查完成:"
    print_message $GREEN "✓ 通过: $((total_checks - failed_checks))"
    print_message $RED "✗ 失败: $failed_checks"
    print_message $BLUE "成功率: ${success_rate}%"
    
    if [ $failed_checks -eq 0 ]; then
        log_alert "INFO" "系统健康检查全部通过"
        return 0
    elif [ $success_rate -ge 80 ]; then
        log_alert "WARNING" "系统健康检查发现 $failed_checks 个问题，成功率 ${success_rate}%"
        return 1
    else
        log_alert "CRITICAL" "系统健康检查发现严重问题，成功率仅 ${success_rate}%"
        return 2
    fi
}

# 函数：显示帮助信息
show_help() {
    echo "个人云笔记系统健康监控脚本 v$SCRIPT_VERSION"
    echo
    echo "用法: $0 [选项]"
    echo
    echo "选项:"
    echo "  --help, -h           显示帮助信息"
    echo "  --version, -v        显示版本信息"
    echo "  --full               运行完整健康检查"
    echo "  --cpu                仅检查CPU使用率"
    echo "  --memory             仅检查内存使用率"
    echo "  --disk               仅检查磁盘使用率"
    echo "  --services           仅检查服务状态"
    echo "  --http               仅检查HTTP响应"
    echo "  --database           仅检查数据库连接"
    echo "  --report             生成健康报告"
    echo "  --daemon             守护进程模式"
    echo "  --interval SECONDS   监控间隔（守护进程模式）"
    echo
    echo "示例:"
    echo "  $0 --full            # 运行完整健康检查"
    echo "  $0 --cpu --memory    # 仅检查CPU和内存"
    echo "  $0 --daemon --interval 300  # 每5分钟检查一次"
    echo
}

# 主函数
main() {
    # 处理命令行参数
    local run_full=false
    local check_cpu=false
    local check_memory=false
    local check_disk=false
    local check_services=false
    local check_http=false
    local check_database=false
    local generate_report=false
    local daemon_mode=false
    local monitor_interval=300
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            --help|-h)
                show_help
                exit 0
                ;;
            --version|-v)
                echo "个人云笔记健康监控脚本 v$SCRIPT_VERSION"
                exit 0
                ;;
            --full)
                run_full=true
                shift
                ;;
            --cpu)
                check_cpu=true
                shift
                ;;
            --memory)
                check_memory=true
                shift
                ;;
            --disk)
                check_disk=true
                shift
                ;;
            --services)
                check_services=true
                shift
                ;;
            --http)
                check_http=true
                shift
                ;;
            --database)
                check_database=true
                shift
                ;;
            --report)
                generate_report=true
                shift
                ;;
            --daemon)
                daemon_mode=true
                shift
                ;;
            --interval)
                monitor_interval="$2"
                shift 2
                ;;
            *)
                print_message $RED "未知选项: $1"
                show_help
                exit 1
                ;;
        esac
    done
    
    # 创建日志文件
    touch "$MONITOR_LOG" "$ALERT_LOG"
    
    # 守护进程模式
    if [ "$daemon_mode" = true ]; then
        print_message $BLUE "启动守护进程模式，监控间隔: ${monitor_interval}秒"
        
        while true; do
            run_full_health_check >/dev/null 2>&1
            sleep "$monitor_interval"
        done
        
        exit 0
    fi
    
    # 开始监控
    print_message $CYAN "个人云笔记系统健康监控 v$SCRIPT_VERSION"
    print_message $CYAN "========================================"
    
    local exit_code=0
    
    # 执行指定的检查
    if [ "$run_full" = true ]; then
        run_full_health_check
        exit_code=$?
    else
        local any_check=false
        
        if [ "$check_cpu" = true ]; then
            any_check=true
            check_cpu_usage || exit_code=1
        fi
        
        if [ "$check_memory" = true ]; then
            any_check=true
            check_memory_usage || exit_code=1
        fi
        
        if [ "$check_disk" = true ]; then
            any_check=true
            check_disk_usage || exit_code=1
        fi
        
        if [ "$check_services" = true ]; then
            any_check=true
            check_service_status || exit_code=1
        fi
        
        if [ "$check_http" = true ]; then
            any_check=true
            check_http_response_time || exit_code=1
        fi
        
        if [ "$check_database" = true ]; then
            any_check=true
            check_database_connection || exit_code=1
        fi
        
        # 如果没有指定任何检查，运行完整检查
        if [ "$any_check" = false ]; then
            run_full_health_check
            exit_code=$?
        fi
    fi
    
    # 生成报告
    if [ "$generate_report" = true ]; then
        generate_health_report >/dev/null
    fi
    
    exit $exit_code
}

# 运行主函数
main "$@"